/*
 * OpenParts
 * A dynamic-loading components framework for GWT
 * Copyright (C) 2011 Christophe Bouthier  [chris{AT}binary-gastronome{DOT}fr]
 *
 * This work is partially based on work I have done at INRIA (http://www.inria.fr) 
 * in the context of the Qualipso European Project (http://qualipso.org/),
 * The work done at INRIA is Copyright (C) 2006-2011 INRIA
 *
 * This work is distributed under the LGPL version 3
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3
 * as published by the Free Software Foundation. See the GNU
 * Lesser General Public License in LGPL.txt for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package fr.openparts.OpenParts.test.gwt.tests;

import com.google.gwt.junit.client.GWTTestCase;

import fr.openparts.OpenParts.client.OPBindingAction;
import fr.openparts.OpenParts.client.OPParams;
import fr.openparts.OpenParts.client.OPParamsJS;

/**
 * GWT tests for OPBindingAction.
 * 
 * @author Christophe Bouthier [chris{AT}binary-gastronome{DOT}fr]
 * @creation.date 31 December 2009
 */
public class OPBindingActionTestGwt extends GWTTestCase {

    private static final String ACTION_NAME = "dummyAction";
    private static final String MANDATORY_PARAM_1 = "dummyParam1";
    private static final String MANDATORY_PARAM_2 = "dummyParam2";

    private OPBindingAction actionWith;
    private OPBindingAction actionWithout;
    private OPParamsJS params;

    public int displayViewCallCounter = 0; // count call to display view callback

    /**
     * Return the name of the tested GWT Module.
     * 
     * @see com.google.gwt.junit.client.GWTTestCase#getModuleName()
     */
    @Override
    public final String getModuleName() {
        return "fr.openparts.OpenParts.test.gwt.TestOpenParts";
    }

    /**
     * Called at the start of each test to set up the action.
     * 
     * @see com.google.gwt.junit.client.GWTTestCase#gwtSetUp()
     */
    @Override
    protected void gwtSetUp() throws Exception {
        super.gwtSetUp();
        final String[] mandatoryParams = { MANDATORY_PARAM_1, MANDATORY_PARAM_2 };
        actionWith = OPBindingAction.actionFactory(ACTION_NAME, mandatoryParams);
        actionWithout = OPBindingAction.actionFactory(ACTION_NAME);
        params = OPParamsJS.buildFromOPParams(new OPParams());
    }

    /**
     * Test creating an OPBindingAction with mandatory params. Should get something not null.
     */
    public final void testCreatingOPBindingActionWithMandatoryParams() {
        assertNotNull("Binding action shouldn't be null", actionWith);
    }

    /**
     * Test creating an OPBindingAction without mandatory params. Should get something not null.
     */
    public final void testCreatingOPBindingActionWithoutMandatoryParams() {
        assertNotNull("Binding action shouldn't be null", actionWithout);
    }

    /**
     * Test creating an OPBindingAction with a null action name. Should raise an assertion error.
     */
    public final void testCreatingOPBindingActionWithNullActionName() {
        if (assertionsAreEnabled()) {
            try {
                OPBindingAction.actionFactory(null);
                fail("OPBindingAction should raise an assertion error when creating a action with null action name.");
            } catch (AssertionError e) {
                assertTrue("Error should be an AssertionError", e instanceof AssertionError);
            }
        }
    }

    /**
     * Test creating an OPBindingAction with a null action name and not null mandatory params. Should raise an assertion error.
     */
    public final void testCreatingOPBindingActionWithNullActionNameAndNotNullMandatoryParams() {
        if (assertionsAreEnabled()) {
            try {
                final String[] mandatory = { MANDATORY_PARAM_1 };
                OPBindingAction.actionFactory(null, mandatory);
                fail("OPBindingAction should raise an assertion error when creating a action with null action name.");
            } catch (AssertionError e) {
                assertTrue("Error should be an AssertionError", e instanceof AssertionError);
            }
        }
    }

    /**
     * Test creating an OPBindingAction with null mandatory params. Should NOT raise an error.
     */
    public final void testCreatingOPBindingActionWithNullMandatoryParams() {
        try {
            OPBindingAction.actionFactory(ACTION_NAME, null);
        } catch (Throwable e) {
            fail("OPBindingAction should NOT raise an error when creating a action with null mandatory params.");
        }
    }

    /**
     * Test getting the action name. Should get ACTION_NAME.
     */
    public final void testGettingActionName() {
        final String actionName = actionWith.getActionName();
        assertNotNull("Action name shouldn't be null", actionName);
        assertEquals("Action name should be dummyAction", ACTION_NAME, actionName);
    }

    /**
     * Test getting all names of mandatory params. Should get the corresponding array.
     */
    public final void testGettingMandatoryParamsNames() {
        final String[] paramsNames = actionWith.getMandatoryParamsNames();
        assertNotNull("Names array shouldn't be null", paramsNames);
        assertEquals("Names array should contains only 2 elements", 2, paramsNames.length);
        boolean paramName1Found = false;
        boolean paramName2Found = false;
        for (String paramName : paramsNames) {
            if (paramName.equals(MANDATORY_PARAM_1)) {
                paramName1Found = true;
            } else if (paramName.equals(MANDATORY_PARAM_2)) {
                paramName2Found = true;
            } else {
                fail("Names array contains an unknown mandatory param name: " + paramName);
            }
        }
        assertTrue("mandatory param name 1 should have been found", paramName1Found);
        assertTrue("mandatory param name 2 should have been found", paramName2Found);
    }

    /**
     * Test getting all names of mandatory params without mandatory params. Should get an empty but not null array.
     */
    public final void testGettingMandatoryParamsNamesWithoutMandatoryParams() {
        final String[] paramsNames = actionWithout.getMandatoryParamsNames();
        assertNotNull("Names array shouldn't be null", paramsNames);
        assertEquals("Names array should be empty", 0, paramsNames.length);
    }

    /**
     * Test calling the display view callback. Should pass the call.
     */
    public final void testCallingDisplayView() {
        assertEquals("no call yet to display view", 0, displayViewCallCounter);
        registerDisplayViewCallback(actionWithout, this);
        final String missingParam = actionWithout.displayView("slotId", params);
        assertEquals("one call to display view", 1, displayViewCallCounter);
        assertNull("there shouldn't be any missing param", missingParam);
    }

    /**
     * Test calling the display view callback with a null sot id. Should raise an assertion error.
     */
    public final void testCallingDisplayViewWithNullSlotID() {
        if (assertionsAreEnabled()) {
            try {
                registerDisplayViewCallback(actionWithout, this);
                actionWithout.displayView(null, params);
                fail("OPBindingAction should raise an assertion error when calling display view with a null slot id.");
            } catch (AssertionError e) {
                assertTrue("Error should be an AssertionError", e instanceof AssertionError);
            }
        }
    }

    /**
     * Test calling the display view callback with a null params. Should NOT get an exception.
     */
    public final void testCallingDisplayViewWithNullParams() {
        try {
            registerDisplayViewCallback(actionWithout, this);
            actionWithout.displayView("slotId", null);
            assertTrue("It should be possible to call display view callback with null params.", true);
        } catch (Throwable e) {
            fail("OPBindingAction should NOT raise an exception when calling display view with a null params.");
        }
    }

    /**
     * Test calling the display view callback without setting first the view callback. Should NOT get an exception.
     */
    public final void testCallingDisplayViewWithoutCallback() {
        try {
            actionWithout.displayView("slotId", params);
            assertTrue("Trying to display a view without binding its display view callback just do nothing.", true);
        } catch (Throwable e) {
            fail("OPBindingAction should NOT raise an exception when calling display view without first having a display view callback.");
        }
    }

    /**
     * Test calling the display view with good mandatory params. Should not get a missing param.
     */
    public final void testCallingDisplayViewWithMandatoryParams() {
        final OPParamsJS paramsJs = OPParamsJS.buildFromOPParams(new OPParams(MANDATORY_PARAM_1, "dummyValue1", MANDATORY_PARAM_2, "dummyValue2"));
        registerDisplayViewCallback(actionWith, this);
        final String missingParam = actionWith.displayView("slotId", paramsJs);
        assertNull("there shouldn't be any missing param", missingParam);
    }

    /**
     * Test calling the display view with missing mandatory params. Should get the missing param name.
     */
    public final void testCallingDisplayViewWithMissingMandatoryParams() {
        final OPParamsJS paramsJs = OPParamsJS.buildFromOPParams(new OPParams(MANDATORY_PARAM_1, "dummyValue1"));
        registerDisplayViewCallback(actionWith, this);
        final String missingParam = actionWith.displayView("slotId", paramsJs);
        assertNotNull("there should be a missing param", missingParam);
        assertEquals("missing param should be " + MANDATORY_PARAM_2, MANDATORY_PARAM_2, missingParam);
    }

    /**
     * Test calling the display view with null mandatory params. Should raise an exception.
     */
    public final void testCallingDisplayViewWithNullMandatoryParams() {
        registerDisplayViewCallback(actionWith, this);
        final String missingParam = actionWith.displayView("slotId", null);
        assertNotNull("there should be a missing param", missingParam);
        assertEquals("missing param should be " + MANDATORY_PARAM_1, MANDATORY_PARAM_1, missingParam);
    }

    /**
     * Helper method to add a display view callback to the action. The display view callback just increment the public displayViewCallCounter of this class.
     * 
     * @param action
     *            the action to add the display view callback.
     * @param testClass
     *            the test class to which the display view callback will increment the public displayViewCallCounter.
     */
    private final native void registerDisplayViewCallback(final OPBindingAction action, final OPBindingActionTestGwt testClass) /*-{
        action.displayView = function(slotId, params) {
        return testClass.@fr.openparts.OpenParts.test.gwt.tests.OPBindingActionTestGwt::incrementCounter(Ljava/lang/String;Lfr/openparts/OpenParts/client/OPParamsJS;)(slotId, params);
        }
    }-*/;

    /**
     * Helper method simulating a display view callback, just incrementing the public displayViewCallCounter.
     * 
     * @param slotId
     *            the passed slotId
     * @param params
     *            the passed params
     */
    public final void incrementCounter(final String slotId, final OPParamsJS params) {
        assert slotId != null;
        ++displayViewCallCounter;
    }

    /**
     * Check if assertions are enabled.
     * 
     * @return true if assertions are enabled, false otherwise
     */
    private final boolean assertionsAreEnabled() {
        try {
            assert false;
            return false;
        } catch (AssertionError e) {
            return true;
        }
    }

}
